#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>

#include <iostream>
#include <string>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <sstream>
#include <istream>
#include "math.h"

#include "LinkedList.h"
#include <time.h>
#include "RNDFEntry.h"

using namespace std;

//define log file name
char logfilename[256] = "/usr/local/bin/log/obugid_mprlog_";

//define connection file
#define CONNFILE "/usr/local/bin/connection.txt"


//socket settings
#define GIDPORT 5799
//#define PMTPORT 15020

#define BROADCAST_ADDR "192.168.255.255"

//define RNDF settings
#define MAX_RNDF 5

//define faciliy type
#define RAMP 0
#define INTERSECTION 1

#define numPhases 8

#define MAX_POINTS 10 //maximum rndf points for each segment
#define MAX_LANES 3
#define MAX_SEGMENTS 10
#define MAX_SIGNALS 10

int Global;
const long timeinterval=30;

LinkedList<RNDFEntry> RNDF_List;

//# of current rndf files
int NORNDF=0;//total number of current RNDF files
//int activeGID = 0; //number of current active GID file
char curRNDFname[MAX_RNDF][64];
char predir [64] = "/usr/local/bin/";

int outputlog(char *output);
bool FindInReqList(LinkedList<RNDFEntry> &ReqList, RNDFEntry TestEntry);

void PrintList2File(char *Filename,LinkedList<RNDFEntry> &ReqList)
    {
    ofstream fs;
    //iostream fs;
    fs.open(Filename);
    if(fs.good())
        {
        if (!ReqList.ListEmpty())
            {
            ReqList.Reset();
            while(!ReqList.EndOfList())
                {
                fs<<ReqList.Data().RNDF_name<<" "<<ReqList.Data().AtTime<<endl;
                ReqList.Next();
                }            
            }
        }
    else
        {
        perror("Error occurs when open a file to write the linked list");
        }
    fs.close();
    }
void PrintList(LinkedList<RNDFEntry> &ReqList)
    {
    if (ReqList.ListEmpty())
        {
        cout<<"NO entry in the list!\n";
        } 
    else
        {
        ReqList.Reset();
        while(!ReqList.EndOfList())
            {
            cout<<ReqList.Data();
            ReqList.Next();
            }
        cout<<"!!!!List print OVER!!!"<<endl<<endl;
        }
    }
bool FindInReqList(LinkedList<RNDFEntry> &ReqList, RNDFEntry TestEntry)
    {
    ReqList.Reset();

    bool temp=false;

    if(ReqList.ListEmpty()) 
        {
        return temp;
        }
    else
        {

        while(!ReqList.EndOfList())
            {
            if(ReqList.Data().RNDF_name == TestEntry.RNDF_name)
                {
                return true;
                } 

            ReqList.Next();       
            }
        }

    return temp;
    }

//------------Time stamp------------------------------------------------
void     xTimeStamp( char * pc_TimeStamp_ )
    {
    struct tm  * ps_Time;
    time_t       i_CurrentTime;
    char         ac_TmpStr[256];

    i_CurrentTime =  time(NULL);
    ps_Time = localtime( &i_CurrentTime );

    //year
    sprintf(ac_TmpStr, "%d", ps_Time->tm_year + 1900);
    strcpy(pc_TimeStamp_, ac_TmpStr);

    //month
    sprintf(ac_TmpStr, "_%d", ps_Time->tm_mon + 1 );
    strcat(pc_TimeStamp_, ac_TmpStr);

    //day
    sprintf(ac_TmpStr, "_%d", ps_Time->tm_mday );
    strcat(pc_TimeStamp_, ac_TmpStr);

    //hour
    sprintf(ac_TmpStr, "_%d", ps_Time->tm_hour  );
    strcat(pc_TimeStamp_, ac_TmpStr);

    //min
    sprintf(ac_TmpStr, "_%d", ps_Time->tm_min );
    strcat(pc_TimeStamp_, ac_TmpStr);

    //sec
    sprintf(ac_TmpStr, "_%d", ps_Time->tm_sec );
    strcat(pc_TimeStamp_, ac_TmpStr);
    }


//end-------------------------------------------------------------------

int outputlog(char *output)
    {
    FILE * stream = fopen( logfilename, "r" );
    fseek( stream, 0L, SEEK_END );
    long endPos = ftell( stream );
    fclose( stream );

    std::fstream fs;
    if (endPos <1000000)
        fs.open(logfilename, std::ios::out | std::ios::app);
    else
        fs.open(logfilename, std::ios::out | std::ios::trunc);
    if (!fs || !fs.good())
        {
        std::cout << "could not open file!\n";
        return -1;
        }
    fs << output << std::endl;

    if (fs.fail())
        {
        std::cout << "failed to append to file!\n";
        return -1;
        }


    return 1;
    }

int main ( int argc, char* argv[] )
    {
    //Init log file
    //------log file name with Time stamp---------------------------
    char timestamp[128];
    char tt[256];
    char strbuf[256];
    xTimeStamp(timestamp);
    strcat(logfilename,timestamp);
    strcat(logfilename,".log");
    //------end log file name-----------------------------------
    //----init requests.txt-------------------------
    char temp2[64];
    sprintf(temp2,"Num_request -1");
    strcpy(tt,"");
    strcat(tt,predir);
    strcat(tt,"requests.txt");
    fstream fs_req;
    fs_req.open(tt, std::ios::out | std::ios::trunc );
    fs_req<<temp2;


    //------------------------------------------------------
    fstream fs;
    fs.open(logfilename, std::ios::out | std::ios::trunc );
    fs << "obugid is running...." << std::endl;

    char pBuf[256];
    char sendMSG[256];

    std::cout << "obugid is running....\n";
    std::cout << "Delete all the old GID files....\n";
    //system("\\rm /usr/local/bin/*.rndf");
    std::cout << "Complete....\n";

    int sockfd;

    struct sockaddr_in sendaddr;
    struct sockaddr_in recvaddr;
    int numbytes;
    int addr_len;
    int broadcast=1;
    char toRSUMsg[] ="Broadcast message from OBU!";

    if((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
        {
        perror("socket");
        exit(1);
        }
    if((setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,
        &broadcast,sizeof broadcast)) == -1)
        {
        perror("setsockopt - SO_SOCKET ");
        exit(1);
        }

    printf("Socket created\n");

    recvaddr.sin_family = AF_INET;
    recvaddr.sin_port = htons(GIDPORT);
    recvaddr.sin_addr.s_addr = INADDR_ANY; //inet_addr(CLIENT_ADDR);//
    memset(recvaddr.sin_zero,'\0',sizeof recvaddr.sin_zero);
    if(bind(sockfd, (struct sockaddr*) &recvaddr, sizeof recvaddr) == -1)
        {
        perror("bind");
        exit(1);
        }

    addr_len = sizeof sendaddr;

    //RSU
    //search for broadcasted GID(RNDF file)

    char aa[256];
    char bb[256];
    //int count = 0;
    char rndf_file[128]="/usr/local/bin/RNDF.txt";

    char temp[30];
    char files[10240];
    bool ishead = false; //looking for the head of rndf file.

    //----------------------------get RNDF files----------------------------------------
    NORNDF = 0; //init number of total rndf files
    std::cout << "Init the new GID files....\n";
    //activeGID = 0;//init current active GID files

    while ( true )
        {
        //start to receive RNDF data from RSU
        if ((numbytes = recvfrom(sockfd, files, sizeof files, 0,
            (struct sockaddr *)&sendaddr, (socklen_t *)&addr_len)) == -1)
            {
            perror("recvfrom");
            exit(1);
            }
        else
            {
            time_t tt0; //initial absoute time t0
            tt0 = time (NULL);

            std::fstream connfile;
            connfile.open(CONNFILE, std::ios::out | std::ios::trunc );
            connfile<<tt0<< std::endl;

            }
        sscanf(files,"%s %s",aa,bb);
        if (strcmp(aa,"RNDF_name")==0)
            {
            /*
            if (NORNDF==0)
            {
            NORNDF++;
            strcpy(curRNDFname[0],bb);
            std::cout << "Got a GID from RSU...\n";
            std::cout <<bb<<std::endl;
            outputlog("Got a GID from RSU...");
            outputlog(bb);
            //save the file
            strcpy(tt,"");
            strcat(tt,predir);
            strcat(tt,bb);
            std::fstream fs;
            fs.open(tt, std::ios::out | std::ios::trunc );
            fs<<files;
            }
            else
            {
            bool isidentical = false;
            for (int i=0;i<NORNDF;i++)
            {
            if (strcmp(curRNDFname[i],bb)==0)	isidentical=true;

            }
            if (isidentical==false)
            {
            NORNDF++;
            strcpy(curRNDFname[NORNDF-1],bb);
            std::cout << "Got a GID from RSU...\n";
            std::cout <<bb<<std::endl;
            outputlog("Got a GID from RSU...");
            outputlog(bb);
            //save the file
            strcpy(tt,"");
            strcat(tt,predir);
            strcat(tt,bb);

            std::fstream fs;
            fs.open(tt, std::ios::out | std::ios::trunc );
            fs<<files;
            }
            }
            */

            fstream fss;
            fss.open(rndf_file);
            time_t start;
            string lineread;
            char rndfname[128];
            int attime;
            RNDF_List.ClearList();
            //---- Read all the RNDF entry in RNDF.txt files-----//
            if(fss.good())
                {
                while(!fss.eof())
                    {         
                    getline(fss,lineread);
                    if(lineread.size()!=0)
                        {
                        if(sscanf(lineread.c_str(),"%s %d",rndfname,&attime)!=2)
                            {
                            perror("Not all fields are assigned.");
                            outputlog("Not all fields are assigned.");
                            exit(1);
                            }
                        else
                            {
                            RNDFEntry e(rndfname,attime);
                            RNDF_List.InsertAfter(e);
                            cout<<e;
                            }                

                        }
                    }
                }
            else
                {
                perror("CANNOT opent the RNDF.txt file.");
                outputlog("CANNOT opent the RNDF.txt file.");
                exit(1);
                }
            fss.close();
            PrintList(RNDF_List);
            //------------Handle the received RNDF file---------        
            RNDFEntry e_temp(aa,start);
            RNDF_List.Reset();

            strcpy(tt,"");
            strcat(tt,predir);
            strcat(tt,bb);

            if(RNDF_List.ListEmpty())
                {
                RNDF_List.InsertAfter(e_temp);

                fstream fs;
                fs.open(tt, std::ios::out | std::ios::trunc );
                fs<<files;
                fs.close();
                }
            else
                {
                if(!FindInReqList(RNDF_List,e_temp))
                    {
                    RNDF_List.InsertRear(e_temp);
                    //----- Save the RNDF file.----

                    fstream fs;
                    fs.open(tt, std::ios::out | std::ios::trunc );
                    fs<<files;
                    fs.close();
                    }
                else
                    {
                    RNDF_List.Reset();

                    while(!RNDF_List.EndOfList())
                        {
                        if(RNDF_List.Data().RNDF_name==e_temp.RNDF_name)
                            {
                            RNDF_List.Data().AtTime=e_temp.AtTime;
                            RNDF_List.Next();
                            }
                        else
                            {
                            if(RNDF_List.Data().AtTime+timeinterval<e_temp.AtTime)
                                {
                                RNDF_List.DeleteAt();

                                strcpy(strbuf,"\\\\rm ");     
                                //system("\\rm /usr/local/bin/*.rndf");
                                strcat(strbuf,tt);
                                system(strbuf);
                                }
                            else
                                {
                                RNDF_List.Next();
                                }
                            }

                        }
                    }
                }
            PrintList(RNDF_List);
            //-------End of Handle the received RNDF file-----
            }
        else if (strcmp(aa,"Signal_status")==0)
            {//get signal status


            //save the file
            strcpy(tt,"");
            strcat(tt,predir);
            strcat(tt,"signal.txt");
            std::fstream fs;
            fs.open(tt, std::ios::out | std::ios::trunc );
            fs<<files;


            }

        else if (strcmp(aa,"Num_request")==0)
            {//get request table


            //save the file
            strcpy(tt,"");
            strcat(tt,predir);
            strcat(tt,"requests.txt");
            std::fstream fs;
            fs.open(tt, std::ios::out | std::ios::trunc );
            fs<<files;


            }



        }//while(true)











    }